package com.jxpanda.spring.module.wechat.component.message;

import com.jxpanda.spring.module.wechat.component.message.handler.WechatReceivedXmlMessageHandler;
import com.jxpanda.spring.module.wechat.component.message.handler.WechatReceivedXmlMessageHandlers;
import com.jxpanda.spring.module.wechat.component.message.pojo.WechatXmlMessage;
import com.jxpanda.spring.module.wechat.component.message.pojo.xml.WechatXmlTextMessage;
import com.jxpanda.spring.module.wechat.toolkit.CorpMessageKit;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.StopWatch;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;

import java.nio.charset.StandardCharsets;
import java.util.Base64;

/**
 * @author Panda
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class WechatCorpMessageService {

    private final WechatReceivedXmlMessageHandlers wechatReceivedXmlMessageHandlers;

    /**
     * 校验回调URL的函数。
     * 该方法通过调用传入的WechatCallbackProcessor对象的decryptEcho方法，来验证来自微信的回调信息的合法性。
     *
     * @param echoStr                 微信发送过来的加密字符串，用于验证回调URL的合法性。
     * @param wechatCallbackProcessor WechatCallbackProcessor对象，用于处理微信回调信息的解密等操作。
     * @return 返回解密后的echoStr字符串。如果解密成功，则返回的字符串与微信发送的echoStr原文相同。
     */
    public Mono<String> callbackUrlVerify(String echoStr, WechatCallbackProcessor wechatCallbackProcessor) {
        // 调用WechatCallbackProcessor对象的decryptEcho方法解密echoStr并返回
        return Mono.just(wechatCallbackProcessor.decryptEcho(echoStr));
    }


    /**
     * 处理微信回调消息。
     * 使用给定的请求体和处理器，解密并处理接收到的消息，然后回复一个标准的文本消息。
     *
     * @param requestBody             接收到的微信回调请求体内容。
     * @param wechatCallbackProcessor 微信回调处理器，用于解密消息和加密回复消息。
     * @return 返回处理后的回复消息体。
     */
    public Mono<String> callbackMessageProcess(String requestBody, WechatCallbackProcessor wechatCallbackProcessor) {
        // 记录传入的微信回调处理器信息
        log.info("[WECHAT CALLBACK PROCESSOR] Process param is: \n {}", wechatCallbackProcessor);
        log.info("[WECHAT CALLBACK PROCESSOR] Process request body is: \n {}", requestBody);
        // 微信云托管那边会把XML格式的日志直接吞掉，这里尝试用base64编码打印出来看看会不会有
        log.info("[WECHAT CALLBACK PROCESSOR] Process request body base64 is: \n {}", Base64.getEncoder().encodeToString(requestBody.getBytes(StandardCharsets.UTF_8)));
        // 解密接收到的消息
        String receiveMessage = wechatCallbackProcessor.decryptMessage(requestBody);
        // 解析接收到的XML消息
        WechatXmlMessage receivedXmlMessage = CorpMessageKit.parseXmlMessage(receiveMessage);
        // 确保接收到的消息不为空
        Assert.notNull(receivedXmlMessage, "ReceivedXmlMessage is null");
        // FIXME: 使用Mono.fromRunnable函数可以异步执行逻辑
        //  由于消息的处理是要调用AI的接口的，现在AI的接口调用时间非常长
        //  所以消息的实际处理需要异步执行，不然用户端等待时间会非常长
        Mono.fromRunnable(() -> this.handleMessage(receivedXmlMessage))
                .subscribeOn(Schedulers.boundedElastic())
                .subscribe();
        // 构建回复消息
        WechatXmlTextMessage replyTextMessage = WechatXmlTextMessage.builder()
                .toUserName(receivedXmlMessage.getFromUserName())
                .fromUserName(receivedXmlMessage.getToUserName())
                .createTime(String.valueOf(System.currentTimeMillis()))
                .content("消息已收到，正在处理中，处理完毕后会通知您！")
                .build();
        // 加密并构建回复消息体
        String replyMessage = wechatCallbackProcessor.replyMessage(replyTextMessage);
        // 记录回复消息内容
        log.info("replyMessage: {}", replyMessage);
        // 返回回复消息体
        return Mono.just(replyMessage);
    }


    /**
     * 异步处理接收到的微信XML消息。
     *
     * @param message 待处理的微信XML消息对象，不应为null。
     *                包含消息类型，用于找到对应的处理策略。
     *                如果消息对象为null，则不执行任何操作。
     */
    public void handleMessage(WechatXmlMessage message) {
        if (message != null) {
            // 根据消息类型获取对应的处理handler
            WechatReceivedXmlMessageHandler<WechatXmlMessage> handler = wechatReceivedXmlMessageHandlers.getHandler(message.getMsgType());
            if (handler != null) {
                log.info("handle message use handler: {}", handler);
                // 记录消息处理时间
                StopWatch messageHandleWatch = new StopWatch("MessageHandleWatch");
                messageHandleWatch.start();
                // 实际处理消息
                handler.apply(message)
                        .doFinally(signalType -> {
                            messageHandleWatch.stop();
                            log.info("handle message success, cost time: {}ms", messageHandleWatch.getTotalTimeMillis());
                        })
                        .subscribe();
            }
        }
    }


}
